/* Endless Code Copyright (C) 2009 Jingul,Kim
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
 */
package ec.gfx.font;

import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.HashMap;

import ec.gfx.GL;
import ec.util.CMath;

/**
 * Using Freetype Library
 * 
 * @since 2010
 * @version 1.0
 * @author <a href="mailto:jingul.kim@gmail.com">Jingul,Kim</a>
 */
public class Font {
	private HashMap<Character, Glyph> map = new HashMap<Character, Glyph>();

	private static final int SIZE = 1024;

	private IntBuffer textures = ByteBuffer.allocateDirect(SIZE * Integer.SIZE)
			.asIntBuffer();
	
	private int face;

	private int size;
	
	private boolean request;

	public Font(String path) {
		this(path, 10);
	}
	
	public Font(String path, int size) {
		face = create(path);

		setSize(size);
	}

	/**
	 * 글꼴 크기 변경
	 * @param size
	 */
	public void setSize(int size) {
		this.size = size;

		setSize(face, size);
		init();
	}
	
	/**
	 * 굵은 효과 추가
	 */
	public void setBold() {
		request = true;
	}

	public int getSize() {
		return size;
	}

	public Glyph getGlyph(char c) {
		Glyph glyph = map.get(c);

		if (glyph == null) {
			glyph = createGlyph(c);
			map.put(c, glyph);
			
			return glyph;
		}

		return glyph;
	}

	private Glyph createGlyph(char c) {
		final int cnt = map.size();
		
		int list = GL.genLists(1);
		
		Glyph glyph = new Glyph(face, list, c);
		ByteBuffer buf = glyph.render(face);
		
		int w = glyph.getWidth(face);
		int h = glyph.getHeight(face);
		
		
		int tx_w = CMath.next_p2(w); 
		int tx_h = CMath.next_p2(h);
		
		ByteBuffer extendBuf = ByteBuffer.allocateDirect(2 * tx_w * tx_h);

		for (int j = 0; j < tx_h; j++) {
			for (int i = 0; i < tx_w; i++) {
				byte b = (i >= w || j >= h) ? 0 : buf.get(i + w * j);
				extendBuf.put(2 * (i + j * tx_w), b);
				extendBuf.put(2 * (i + j * tx_w) + 1, b);
			}
		}
		
		GL.bindTexture(GL.GL_TEXTURE_2D, textures.get(cnt));
		GL.texParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
				GL.GL_LINEAR);
		GL.texParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
				GL.GL_LINEAR);

		GL.texImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, tx_w, tx_h, 0,
				GL.GL_LUMINANCE_ALPHA, GL.GL_UNSIGNED_BYTE, extendBuf);

		extendBuf.clear();
		
		float x = (float)w / (float)tx_w;
		float y = (float)h / (float)tx_h;

		GL.newList(list, GL.GL_COMPILE);
		GL.bindTexture(GL.GL_TEXTURE_2D, textures.get(cnt));
		
		GL.pushMatrix();
		
		// bearingX, Y
		GL.translatef(glyph.getLeft(face), glyph.getBottom(face), 0);

		GL.begin(GL.GL_QUADS);
		GL.texCoord2f(0, 0);
		GL.vertex2f(0, h);
		GL.texCoord2f(0, y);
		GL.vertex2f(0, 0);
		GL.texCoord2f(x, y);
		GL.vertex2f(w, 0);
		GL.texCoord2f(x, 0);
		GL.vertex2f(w, h);
		GL.end();
		GL.popMatrix();

		// AdvancedX, Y
		GL.translatef(glyph.getAdvX(face), glyph.getAdvY(face), 0);
		GL.endList();
		
		return glyph;
	}

	private native void setSize(int face, int size);

	private native int create(String path);

	private native void dispose(int face);
	
//	private native void bolden(int face);

	public void dispose() {
		dispose(face);
	}

	public int getFace() {
		return face;
	}

	private void init() {
		GL.genTextures(SIZE, textures);
	}
}
